﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows.Threading;
using BrainTechLLC;
using BrainTechLLC.FilterExtensions;
// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using ImageAppDataModels;
using ImageAppViewModels;

namespace CompareMultiple.Plugin
{
    public partial class CompareMultipleImagesView : UserControl
    {
        public static SimilarityGroupHandler _groupHandler = new SimilarityGroupHandler();

        public void BackgroundWorkerThreadFindSimilarity()
        {
            SimilarityQueueAndCache.SimilarityCache.Clear();
            SimilarityQueueAndCache.SimilarityQueue.Clear();
            int total = Factorial(_viewModels.Count - 1) + 1;
            int count = 0;
            int simCacheMax = Settings.Values.SimilarityCacheMax;
            int simCacheRemove = Settings.Values.SimilarityCacheToRemove;

            if (simCacheMax == 0)
                simCacheMax = 64;

            if (simCacheRemove == 0)
                simCacheRemove = 14;

            List<SimilarityGrouping> similarityGroups = new List<SimilarityGrouping>();
               
            try
            {
                for (int n = 0; n < _viewModels.Count; n++)
                {
                    var vm = _viewModels[n];

                    for (int i = n + 1; i < _viewModels.Count; i++)
                    {
                        count++;
                        var vmOther = _viewModels[i];
                        Dispatcher.BeginInvoke(new VoidDelegate(() => SetFindSimilarSearching(count, total)));

                        if (SimilarityQueueAndCache.SimilarityCache.Count > simCacheMax)
                        {
                            while (SimilarityQueueAndCache.SimilarityQueue.Count > simCacheMax - simCacheRemove)
                            {
                                SimilarityCacheEntry entry;
                                if (SimilarityQueueAndCache.SimilarityQueue.Dequeue(out entry))
                                {
                                    SimilarityQueueAndCache.SimilarityCache.Remove(entry.FileName);
                                    // Possible memory leak here with entry - may need to clear
                                }
                            }
                        }

                        try
                        {
                            double d = vm.GetSimilarity(vmOther, false);
                            Dispatcher.BeginInvoke(new ThreeParamDelegate((o1, o2, o3) =>
                            {
                                var vmInternal = o1 as FileImageViewModel;
                                var vmInternalOther = o2 as FileImageViewModel;
                                double val = (double)o3;
                                vmInternal.SetSimilarityWith(vmInternalOther.FileImage as FileImage, val);
                                vmInternalOther.SetSimilarityWith(vmInternal.FileImage as FileImage, val);
                            }), vm, vmOther, d);
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.ToString());
                            Debug.Assert(false);
                            //Console.WriteLine(ex.ToString());
                        }
                    }
                }

                // Create similarity groups
                for (int n = 0; n < _viewModels.Count; n++)
                {
                    SimilarityGrouping group = null;
                    var vm = _viewModels[n];

                    //n + 1
                    for (int i = 0; i < _viewModels.Count; i++)
                    {
                        if (i == n)
                            continue;

                        var vmOther = _viewModels[i];
                        double d = vm.GetSimilarityWith(vmOther.FileName);

                        if (!double.IsNaN(d) && d != 0D && d < Settings.Values.SimilarityCutoff)
                        {
                            if (group == null)
                            {
                                group = new SimilarityGrouping();
                                group.GroupName = n.ToString();
                                group.Similar.Add(vm);
                                vm.SimilarityGroups.Add(group.GroupName);
                            }

                            group.Similar.Add(vmOther);
                            vmOther.SimilarityGroups.Add(group.GroupName);
                        }
                    }

                    if (group != null)
                    {
                        similarityGroups.Add(group);
                    }
                }

                for (int n = 0; n < _viewModels.Count; n++)
                {
                    var vm = _viewModels[n];
                    for (int i = 0; i < _viewModels.Count; i++)
                    {
                        if (n == i)
                            continue;

                        var vmOther = _viewModels[i];
                        var overlap = vm.GetOverlap(vm.SimilarityGroups, vmOther.SimilarityGroups);

                        if (overlap.Count > 1)
                        {
                            bool allHaveSubset = true;

                            for (int x = 0; x < _viewModels.Count; x++)
                            {
                                var vmThird = _viewModels[x];
                                if (vmThird.HasAny(vmThird.SimilarityGroups, overlap))
                                {
                                    if (!vmThird.ContainsSubset(vmThird.SimilarityGroups, overlap))
                                    {
                                        allHaveSubset = false;
                                        break;
                                    }
                                }
                            }

                            if (allHaveSubset)
                            {
                                var toKeep = overlap[0];
                                for (int x = 0; x < _viewModels.Count; x++)
                                {
                                    var vmThird = _viewModels[x];
                                    vmThird.SimilarityGroups.RemoveAll((s) => overlap.Contains(s) && s != toKeep);
                                }
                            }
                        }
                    }
                }

                for (int n = 0; n < _viewModels.Count; n++)
                {
                    var vm = _viewModels[n];

                    Dispatcher.BeginInvoke(
                        new OneParamDelegate((o) => (o as FileImageViewModel).RaiseSimilarityGroupsChanged()), 
                        DispatcherPriority.Background, 
                        vm);
                }

                List<SimilarityGrouping> allGroups = new List<SimilarityGrouping>();
                Dictionary<string, bool> uniqueGroupNames = new Dictionary<string, bool>();

                for (int n = 0; n < _viewModels.Count; n++)
                {
                    var vm = _viewModels[n];
                    
                    for (int i = 0; i < vm.SimilarityGroups.Count; i++)
                    {
                        var grp = vm.SimilarityGroups[i];

                        if (!uniqueGroupNames.ContainsKey(grp))
                        {
                            uniqueGroupNames.Add(grp, true);
                        }
                    }
                }

                foreach (var kvp in uniqueGroupNames)
                {
                    SimilarityGrouping newGroup = new SimilarityGrouping() { GroupName = kvp.Key };

                    for (int n = 0; n < _viewModels.Count; n++)
                    {
                        var vm = _viewModels[n];

                        if (vm.SimilarityGroups.Contains(kvp.Key))
                        {
                            newGroup.Similar.Add(vm); 
                        }
                    }

                    allGroups.Add(newGroup);
                }

                _groupHandler.RegisterGroups(allGroups);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }

            
            SimilarityQueueAndCache.SimilarityCache.Clear();
            SimilarityQueueAndCache.SimilarityQueue.Clear();
            _similarityComplete = true;
            Dispatcher.BeginInvoke(new VoidDelegate(() => SetFindSimilarDone()));
            _viewModels.Clear();
            _viewModels = null;
        }
    }
}
